/*
 * SwingDelegate.cpp  0.4.0 / Nov 17, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
#include "SwingDelegate.h"

#include "QApplicationDelegate.h"
#include "QIconAdapter.h"
#include "SwingImageDelegate.h"
#include "SwingUtilitiesDelegate.h"
#include "QStyleManager.h"

#include "com_cute4j_Cute4j.h"
#include "com_cute4j_swing_events_KeyEvent.h"

// SwingDelegate :: Local Macro Helper. ----------------------------------------

#define RETURN_TRANSLATED(qtKey, swingKey) \
          case qtKey: \
            return com_cute4j_swing_events_KeyEvent_##swingKey;

// SwingDelegate :: Local Function. --------------------------------------------

void disabledMessageHandler(QtMsgType type, const char *msg)
{
  Q_UNUSED(type);
  Q_UNUSED(msg);
  // empty method
}

// SwingDelegate :: Static Native Calls. ---------------------------------------

/*!
 * \brief Initializes LabelDelegate class.
 *
 * \param env the JNIEnv pointer 
 * \param cls the Swing class 
 * \param loggerClass the Loger java class
 */
jint SwingDelegate::getVersion(JNIEnv *env, jclass cls, jclass loggerClass)
{
  Q_UNUSED(cls);
  JNILogger::initialize(env, loggerClass);
  return com_cute4j_Cute4j_VERSION;
}

/*!
 * \brief Initializes LabelDelegate class.
 *
 * \param env the JNIEnv pointer 
 * \param cls the Swing class 
 * \param args the application arguments
 * \param colorClass the Swing Color class
 * \param dimensionClass the Swing Dimension class
 * \param rectangleClass the Swing Rectangle class
 * \param fontClass the Swing Font class
 * \param imageClass the Swing Image class
 * \param iconSetClass the Swing IconSet class
 * \param runnableClass the java Runable class
 * \param iteClass the java Invocation Target Exception class
 * \param systemColorPaletteClass the Swing System Color Palette class
 * \param systemFontSetClass the Swing System Font Set class
 */
void SwingDelegate::initialize(JNIEnv *env, jclass cls, jobjectArray args,
                               jclass colorClass, jclass dimensionClass,
                               jclass rectangleClass, jclass fontClass,
                               jclass imageClass, jclass iconSetClass,
                               jclass runnableClass, jclass iteClass,
                               jclass systemColorPaletteClass,
                               jclass systemFontSetClass)
{
  Q_UNUSED(cls);
  JNISwingWrapper::initialize(env);
  QApplicationDelegate::initialize(env, args);
  QStyleManager::initialize(env, systemColorPaletteClass, systemFontSetClass);
  QColorAdapter::initialize(env, colorClass);
  DimensionObject::initialize(env, dimensionClass);
  RectangleObject::initialize(env, rectangleClass);
  QFontAdapter::initialize(env, fontClass);
  SwingImageDelegate::initialize(env, imageClass);
  QIconAdapterEngine::initialize(env, iconSetClass);
  SwingMetaObject::initialize();
  SwingUtilitiesDelegate::initialize(env, runnableClass, iteClass);
}

/*!
 * \brief Disposes Swing.
 *
 * \param env the JNIEnv pointer
 * \param cls the Swing class
 */
void SwingDelegate::dispose(JNIEnv *env, jclass cls)
{
  Q_UNUSED(env);
  Q_UNUSED(cls);
  if (!sm_disposed) {
    sm_disposed = true;
    JNISwingWrapper::sm_disposed = true;
    QApplicationDelegate::dispose();  // always in Swing thread
  }
}

/*!
 * \brief Starts Swing event loop.
 *
 * \param env the JNIEnv pointer 
 * \param cls the Swing class
 * 
 * \return loop exit code
 */
jint SwingDelegate::exec(JNIEnv *env, jclass cls)
{
  Q_UNUSED(env);
  Q_UNUSED(cls);
  // NOTE: This is always in the Swing thread
  return QApplicationAdapter::exec();
}

/*!
 * \brief Exits Swing event loop.
 *
 * \param env the JNIEnv pointer
 * \param cls the Swing class
 * \param returnCode the return code
 */
void SwingDelegate::exit(JNIEnv *env, jclass cls, jint returnCode)
{
  Q_UNUSED(env);
  Q_UNUSED(cls);
  qAppAdapter->exit(returnCode);
}

/*!
 * \brief Processes the events.
 *
 * \param env the JNIEnv pointer 
 * \param cls the Swing class
 */
void SwingDelegate::flush(JNIEnv *env, jclass cls)
{
  Q_UNUSED(cls);
  if (JNISwingWrapper::isSwingThread(env)) {  // no sense for other threads
    qAppAdapter->processEvents();
  }
}

/*!
 * \brief Disables native logging.
 *
 * \param env the JNIEnv pointer 
 * \param cls the Swing class
 */
void SwingDelegate::disableNativeLogging(JNIEnv *env, jclass cls)
{
  Q_UNUSED(env);
  Q_UNUSED(cls);
  qInstallMsgHandler(disabledMessageHandler);
}

/*!
 * \brief Sets translation.
 *
 * \param env the JNIEnv pointer
 * \param cls the Swing class
 * \param fileName the translation file name
 *
 * \return \c true if succeed
 */
jboolean SwingDelegate::setTranslation(JNIEnv *env, jclass cls,
                                       jstring fileName)
{
  Q_UNUSED(cls);
  return qAppAdapter->setTranslation(env, QStringAdapter(env, fileName));
}

/*!
 * \brief Removes translation.
 *
 * \param env the JNIEnv pointer
 * \param cls the Swing class
 */
void SwingDelegate::removeTranslation(JNIEnv *env, jclass cls)
{
  Q_UNUSED(env);
  Q_UNUSED(cls);
  qAppAdapter->removeTranslation(env);
}

/*!
 * \brief Returns translation text for the sourceText.
 *
 * \param env the JNIEnv pointer
 * \param cls the Swing class
 * \param context the translation context
 * \param sourceText the text to be translated
 *
 * \return translation text for the sourceText
 */
jstring SwingDelegate::translate(JNIEnv *env, jclass cls, jstring context,
                                 jstring sourceText)
{
  Q_UNUSED(cls);
  QString translated = QApplication::translate(
                                    QStringAdapter(env, context).toLatin1(),
                                    QStringAdapter(env, sourceText).toLatin1());
  return QStringAdapter::toJString(env, translated);
}

/*!
 * \brief Returns abailable styles.
 *
 * \param env the JNIEnv pointer
 * \param systemStyleClass the SystemStyle class
 * 
 * \return abailable styles
 */
jobjectArray SwingDelegate::availableStyles(JNIEnv *env, jclass cls,
                                            jclass systemStyleClass)
{
   Q_UNUSED(cls);
  QString systemStyleName = qStyleManager->systemStyleName();
  QStringList styleNames = qStyleManager->availableStyleNames();
  jobjectArray availableStyles = JNIWrapper::newObjectArray(env,
                                                            systemStyleClass,
                                                            styleNames.size());
  if (availableStyles) {
    static jmethodID systemStyleConstructor = // lazy caching
           JNIWrapper::getMethodID(env, systemStyleClass, "<init>",
                                   "(Ljava/lang/String;Ljava/lang/String;Z)V");
    if (systemStyleConstructor) {
      for (int i = 0; i < styleNames.size(); i++) {
        QString styleName = styleNames[i];
        QString styleDesctription = styleName + " " +
                                    QApplication::translate("Cute4j", "Style");
        jstring styleNameRef = QStringAdapter::toJString(env, styleName);
        if (!styleNameRef) {
          break;
        }
        jstring styleDesctriptionRef =
                             QStringAdapter::toJString(env, styleDesctription);
        if (!styleDesctriptionRef) {
          break;
        }
        jobject style = JNIWrapper::newObject(env, systemStyleClass,
                                              systemStyleConstructor,
                                              styleNameRef,
                                              styleDesctriptionRef,
                                              styleNames[i] == systemStyleName);
        if (!style) {
          break;
        }
        env->SetObjectArrayElement(availableStyles, i, style);
        env->DeleteLocalRef(styleNameRef);
        env->DeleteLocalRef(styleDesctriptionRef);
        env->DeleteLocalRef(style);
      }
    }
  }
  return availableStyles;
}

/*!
 * \brief Sets the specified style.
 *
 * \param env the JNIEnv pointer 
 * \param cls the Swing java class
 * \param name the style name 
 * 
 * \return \c true if succeed
 */
jboolean SwingDelegate::setStyle(JNIEnv *env, jclass cls, jstring name)
{
  Q_UNUSED(cls);
  return qStyleManager->setStyle(env, name);
}

/*!
 * \brief Sets style sheet.
 *
 * \param env the JNIEnv pointer 
 * \param cls the Swing java class
 * \param styleSheetText the style sheet text
 */
void SwingDelegate::setStyleSheet(JNIEnv *env, jclass cls,
                                 jstring styleSheetText)
{
  Q_UNUSED(cls);
  qAppAdapter->setStyleSheet(env, styleSheetText);
}

/*!
 * \brief Sets application override cursor.
 *
 * \param env the JNIEnv pointer 
 * \param cls the Swing java class
 * \param type the style cursor type
 */
void SwingDelegate::setOverrideCursor(JNIEnv *env, jclass cls, jint type)
{
  Q_UNUSED(cls);
  qAppAdapter->setOverrideCursor(env, type);
}

/*!
 * \brief Changes application override cursor.
 *
 * \param env the JNIEnv pointer 
 * \param cls the Swing java class
 * \param type the style cursor type
 */
void SwingDelegate::changeOverrideCursor(JNIEnv *env, jclass cls, jint type)
{
  Q_UNUSED(cls);
  qAppAdapter->changeOverrideCursor(env, type);
}

/*!
 * \brief Undos last setOverrideCursor().
 *
 * \param env the JNIEnv pointer 
 * \param cls the Swing java class
 */
void SwingDelegate::restoreOverrideCursor(JNIEnv *env, jclass cls)
{
  Q_UNUSED(cls);
  qAppAdapter->restoreOverrideCursor(env);
}

/*!
 * \brief Returns available themes.
 *
 * \param env the JNIEnv pointer 
 * \param systemThemeClass the SystemTheme class
 * 
 * \return available themes
 */
jobjectArray SwingDelegate::availableThemes(JNIEnv *env, jclass cls,
                                            jclass systemThemeClass)
{
  Q_UNUSED(cls);
  QStringList themeNames = qStyleManager->availableThemeNames();
  jobjectArray availableThemes = JNIWrapper::newObjectArray(env,
                                                            systemThemeClass,
                                                            themeNames.size());
  if (availableThemes) {
    static jmethodID systemThemeConstructor = // lazy caching
                       JNIWrapper::getMethodID(env, systemThemeClass, "<init>",
                                               "(Ljava/lang/String;)V");
    if (systemThemeConstructor) {
      for (int i = 0; i < themeNames.size(); i++) {
        jstring themeName = QStringAdapter::toJString(env, themeNames[i]);
        if (!themeName) {
          break;
        }
        jobject theme = JNIWrapper::newObject(env, systemThemeClass,
                                              systemThemeConstructor,
                                              themeName);
        if (!theme) {
          break;
        }
        env->SetObjectArrayElement(availableThemes, i, theme);
        env->DeleteLocalRef(themeName);
        env->DeleteLocalRef(theme);
      }
    }
  }
  return availableThemes;
}

/*!
 * \brief Sets style theme
 *
 * \param env the JNIEnv pointer
 * \param cls the Swing class
 * \param colorPalette the color palette
 * \param fontSet the font set
 */
void SwingDelegate::setTheme(JNIEnv *env, jclass cls, jobject colorPalette,
                             jobject fontSet)
{
  Q_UNUSED(cls);
  qStyleManager->setTheme(env, colorPalette, fontSet);
}

/*!
 * \brief Sets whether or not using system theme
 *
 * \param env the JNIEnv pointer 
 * \param cls the Swing java class
 * \param useSystemTheme \c true if using system theme
 */
void SwingDelegate::setUseSystemTheme(JNIEnv *env, jclass cls,
                                      jboolean useSystemTheme)
{
  Q_UNUSED(cls);
  qStyleManager->setUseSystemTheme(env, useSystemTheme);
}

/*!
 * \brief Displays About Swing message box.
 *
 * \param env the JNIEnv pointer
 * \param cls the Swing class
 * \param version the version of the library
 * \param date the release date
 */
void SwingDelegate::aboutCute4j(JNIEnv *env, jclass cls, jstring version,
                                jstring date)
{
  Q_UNUSED(cls);
  qAppAdapter->aboutCute4j(env, version, date);
}

/*!
 * \brief Displays About Qt message box.
 *
 * \param env the JNIEnv pointer
 * \param cls the Swing class
 */
void SwingDelegate::aboutQt(JNIEnv *env, jclass cls)
{
  Q_UNUSED(cls);
  qAppAdapter->aboutQt(env);
}

/*!
 * \brief Sets custom property.
 *
 * \param env the JNIEnv pointer
 * \param cls the Swing class
 * \param name the property name
 * \param value the property value
 */
void SwingDelegate::setCustomProperty(JNIEnv *env, jclass cls, jstring name,
                                      jstring value)
{
  Q_UNUSED(cls);
  QStyleManager::setCustomProperty(QStringAdapter(env, name),
                                   QStringAdapter(env, value));
}

/*!
 * \brief Returns Swing key char.
 *
 * \param e the Qt QKeyEvent
 *
 * \return Swing key char
 */
ushort SwingDelegate::getSwingKeyChar(QKeyEvent *e)
{
  QString keyText = e->text();
  ushort keyChar;
  if (keyText.isEmpty()) {
    keyChar = 0xFFFF;
  } else {
    keyChar = keyText.at(0).unicode();
  }
  return keyChar;
}

/*!
 * \brief Returns Swing keyboard modifiers.
 *
 * \param modifiers the Qt keyboard modifiers 
 *
 * \return Swing keyboard modifiers 
 */
jint SwingDelegate::toSwingModifiers(Qt::KeyboardModifiers modifiers)
{
  jint swingModifiers = 0;
  if (modifiers & Qt::ShiftModifier) {
    swingModifiers |= 0x1;
  }
  if (modifiers & Qt::ControlModifier) {
    swingModifiers |= 0x2;
  }
  if (modifiers & Qt::MetaModifier) {
    swingModifiers |= 0x4;
  }
  if (modifiers & Qt::AltModifier) {
    swingModifiers |= 0x8;
  }
  return swingModifiers;
}

/*!
 * \brief Converts key code to keyboard modifiers.
 *
 * \param keyCode the key code
 *
 * \return keyboard modifiers 
 */
Qt::KeyboardModifiers SwingDelegate::toKeyboardModifiers(int keyCode)
{
  switch (keyCode) {
    case Qt::Key_Shift:
      return Qt::ShiftModifier;
    case Qt::Key_Control:
      return Qt::ControlModifier;
    case Qt::Key_Meta:
      return Qt::MetaModifier;
    case Qt::Key_Alt:
      return Qt::AltModifier;
    default:
      return 0;
  }
}

/*!
 * \brief Returns Swing mouse button.
 *
 * \param button the Qt mouse button
 *
 * \return Swing mouse button 
 */
jint SwingDelegate::toSwingButton(Qt::MouseButton button)
{
  switch (button) {
    case Qt::LeftButton:
      return 1;
    case Qt::MidButton:
      return 2;
    case Qt::RightButton:
      return 3;
    default:
      return 0;
  }
}

/*!
 * \brief Returns Swing key code.
 *
 * \param keyCode the Qt key code
 *
 * \return Swing key code 
 */
jint SwingDelegate::toSwingKeyCode(int keyCode)
{
  switch (keyCode) {
    RETURN_TRANSLATED(Qt::Key_Escape, VK_ESCAPE)
    RETURN_TRANSLATED(Qt::Key_Tab, VK_TAB)
    RETURN_TRANSLATED(Qt::Key_Backspace, VK_BACK_SPACE)
    RETURN_TRANSLATED(Qt::Key_Return, VK_ENTER)
    RETURN_TRANSLATED(Qt::Key_Enter, VK_ENTER)
    RETURN_TRANSLATED(Qt::Key_Insert, VK_INSERT)
    RETURN_TRANSLATED(Qt::Key_Delete, VK_DELETE)
    RETURN_TRANSLATED(Qt::Key_Pause, VK_PAUSE)
    RETURN_TRANSLATED(Qt::Key_Print, VK_PRINTSCREEN)
    RETURN_TRANSLATED(Qt::Key_Clear, VK_CLEAR)
    RETURN_TRANSLATED(Qt::Key_Home, VK_HOME)
    RETURN_TRANSLATED(Qt::Key_End, VK_END)
    RETURN_TRANSLATED(Qt::Key_Left, VK_LEFT)
    RETURN_TRANSLATED(Qt::Key_Up, VK_UP)
    RETURN_TRANSLATED(Qt::Key_Right, VK_RIGHT)
    RETURN_TRANSLATED(Qt::Key_Down, VK_DOWN)
    RETURN_TRANSLATED(Qt::Key_PageUp, VK_PAGE_UP)
    RETURN_TRANSLATED(Qt::Key_PageDown, VK_PAGE_DOWN)
    RETURN_TRANSLATED(Qt::Key_Shift, VK_SHIFT)
    RETURN_TRANSLATED(Qt::Key_Control, VK_CONTROL)
    RETURN_TRANSLATED(Qt::Key_Meta, VK_META)
    RETURN_TRANSLATED(Qt::Key_Alt, VK_ALT)
    RETURN_TRANSLATED(Qt::Key_CapsLock, VK_CAPS_LOCK)
    RETURN_TRANSLATED(Qt::Key_NumLock, VK_NUM_LOCK)
    RETURN_TRANSLATED(Qt::Key_ScrollLock, VK_NUM_LOCK)
    RETURN_TRANSLATED(Qt::Key_F1, VK_F1)
    RETURN_TRANSLATED(Qt::Key_F2, VK_F2)
    RETURN_TRANSLATED(Qt::Key_F3, VK_F3)
    RETURN_TRANSLATED(Qt::Key_F4, VK_F4)
    RETURN_TRANSLATED(Qt::Key_F5, VK_F5)
    RETURN_TRANSLATED(Qt::Key_F6, VK_F6)
    RETURN_TRANSLATED(Qt::Key_F7, VK_F7)
    RETURN_TRANSLATED(Qt::Key_F8, VK_F8)
    RETURN_TRANSLATED(Qt::Key_F9, VK_F9)
    RETURN_TRANSLATED(Qt::Key_F10, VK_F10)
    RETURN_TRANSLATED(Qt::Key_F11, VK_F11)
    RETURN_TRANSLATED(Qt::Key_F12, VK_F12)
    RETURN_TRANSLATED(Qt::Key_F13, VK_F13)
    RETURN_TRANSLATED(Qt::Key_F14, VK_F14)
    RETURN_TRANSLATED(Qt::Key_F15, VK_F15)
    RETURN_TRANSLATED(Qt::Key_F16, VK_F16)
    RETURN_TRANSLATED(Qt::Key_F17, VK_F17)
    RETURN_TRANSLATED(Qt::Key_F18, VK_F18)
    RETURN_TRANSLATED(Qt::Key_F19, VK_F19)
    RETURN_TRANSLATED(Qt::Key_F20, VK_F20)
    RETURN_TRANSLATED(Qt::Key_F21, VK_F21)
    RETURN_TRANSLATED(Qt::Key_F22, VK_F22)
    RETURN_TRANSLATED(Qt::Key_F23, VK_F23)
    RETURN_TRANSLATED(Qt::Key_F24, VK_F24)
    RETURN_TRANSLATED(Qt::Key_Help, VK_HELP)
    RETURN_TRANSLATED(Qt::Key_Space, VK_SPACE)
    // TODO: Complete the table
  }
  return keyCode;
}

/*!
 * \brief Returns the Qt horizontal alignment.
 *
 * \param alignment the Swing horizontal alignment
 *
 * \return the Qt horizontal alignment
 */
Qt::Alignment SwingDelegate::toHorizontalAlignment(jint alignment)
{
  if ((alignment >= 0) && (alignment <= 11)) { // security check
    return HORIZONTAL_ALIGNMENT[alignment];
  }
  LOG_WARNING("Alignment out of bounds");
  return Qt::AlignLeft;
}

/*!
 * \brief Returns the Qt vertical alignment.
 *
 * \param alignment the Swing vertical alignment
 *
 * \return the Qt vertical alignment
 */
Qt::Alignment SwingDelegate::toVerticalAlignment(jint alignment)
{
  if ((alignment >= 0) && (alignment <= 11)) { // security check
    return VERTICAL_ALIGNMENT[alignment];
  }
  LOG_WARNING("Alignment out of  bounds");
  return Qt::AlignTop;
}

// SwingDelegate :: Private Static Data Member. ----------------------------------

bool SwingDelegate::sm_disposed = false;

// SwingDelegate :: Private Static Constant Data Members. ------------------------

const Qt::Alignment SwingDelegate::HORIZONTAL_ALIGNMENT[] = {
  Qt::AlignHCenter,
  Qt::AlignTop,
  Qt::AlignLeft,
  Qt::AlignBottom,
  Qt::AlignRight,
  0,
  0,
  0,
  0,
  0,
  Qt::AlignLeft,
  Qt::AlignRight,
};
const Qt::Alignment SwingDelegate::VERTICAL_ALIGNMENT[] = {
  Qt::AlignVCenter,
  Qt::AlignTop,
  Qt::AlignLeft,
  Qt::AlignBottom,
  Qt::AlignRight,
  0,
  0,
  0,
  0,
  0,
  Qt::AlignLeft,
  Qt::AlignRight,
};

